/*
 * Copyright (c) 2007, Olof Naessen and Per Larsson
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *    * Redistributions of source code must retain the above copyright notice, 
 *      this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright notice, 
 *      this list of conditions and the following disclaimer in the documentation 
 *      and/or other materials provided with the distribution.
 *    * Neither the name of the Darkbits nor the names of its contributors may be 
 *      used to endorse or promote products derived from this software without 
 *      specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "tilemap.hpp"

#include "voidtile.hpp"
#include "animatedtile.hpp"
#include "startile.hpp"
#include "hostiletile.hpp"
#include "portaltile.hpp"
#include "floorspikestile.hpp"
#include "firetile.hpp"
#include "goaltile.hpp"
#include "breakingtile.hpp"
#include "watertile.hpp"
#include "waterstartile.hpp"
#include "blocktile.hpp"
#include "waterspikestile.hpp"

TileMap::TileMap(unsigned int length)
: mLength(length),
mPortalP1(0),
mPortalP2(0),
mPortalQ1(0),
mPortalQ2(0),
mFrame(0)
{
	mFire = create_bitmap(16, 16);
	clear(mFire);	
	mFireTemp = create_bitmap(16, 16);
	clear(mFireTemp);
}

TileMap::~TileMap()
{
    unsigned int i;
    for (i = 0; i < mMap.size(); i++)
    {
        delete mMap[i];
    }
	
	destroy_bitmap(mFire);
	destroy_bitmap(mFireTemp);
}

void TileMap::setTile(unsigned int type, int x, int y)
{   
    Tile* tile;

    switch (type)
    {
    case 0: 
        tile = new VoidTile(x, y);
        break;
    case 1:
        tile = new BlockTile(x, y);
        break;
    case 2:
        tile = new StarTile(x, y);
        break;
    case 3:
        tile = new HostileTile(x, y, "spikes.bmp");
        break;
     case 4:
        tile = new WaterTile(x, y, "watertile.bmp");
		break;
	case 6:
		tile = new FireTile(x, y, mFire);
		break;
    case 7:
		tile = new FloorSpikeTile(x, y, "floorspikes.bmp");
		break;
    case 8:
		tile = new WaterStarTile(x, y);
		break;
    case 9:
		tile = new BreakingTile(x, y);
		break;
    case 10:
		tile = new WaterSpikesTile(x, y);
		break;
    case 11:
        {
		DoorButtonTile* doorTileButton = new DoorButtonTile(x, y, "doorbutton.bmp");
        tile = doorTileButton;
        mDoorButtons.push_back(doorTileButton);
		break;
        }
    case 12:
        {
         DoorTile* doorTile = new DoorTile(x, y, "doortile.bmp");
         tile = doorTile;
         doorTile->setClosed(false);
         mDoors.push_back(doorTile);
		break;
        }
    case 13:
         if (mPortalP1 == 0)
         {
            mPortalP1 = new PortalTile(x, y, "portal.bmp");
            tile = mPortalP1;
         }
         else
         {
            mPortalP2 = new PortalTile(x, y, "portal.bmp");
            tile = mPortalP2;
            mPortalP1->setDestinationPortal(mPortalP2);
            mPortalP2->setDestinationPortal(mPortalP1);
         }
        break;
	case 14:
         if (mPortalQ1 == 0)
         {
            mPortalQ1 = new PortalTile(x, y, "portalQ.bmp");
            tile = mPortalQ1;
         }
         else
         {
            mPortalQ2 = new PortalTile(x, y, "portalQ.bmp");
            tile = mPortalQ2;
            mPortalQ1->setDestinationPortal(mPortalQ2);
            mPortalQ2->setDestinationPortal(mPortalQ1);
         }
        break;
     case 15:
        {
         DoorTile* doorTile = new DoorTile(x, y, "doortile.bmp");
         tile = doorTile;
         mDoors.push_back(doorTile);
		break;
        }
     case 16:
		tile = new GoalTile(x, y, "goaltile.bmp");
		break;
    default:
        throw std::string("Unknown tile type!" + type);
    }
    
    mMap.push_back(tile);
}

Tile *TileMap::getTileAtPixel(int x, int y)
{
	return getTile(x / 16, y / 16);
}

Tile *TileMap::getTile(int x, int y)
{
	if (x < 0)
	{
		x = 0;
	}
	if (x >= mLength)
	{
		x = mLength - 1;
	}
	if (y < 0)
	{
		y = 0;
	}
	if (y > 14)
	{
		y = 14;
	}
	return mMap[x + y * mLength];
}

void TileMap::draw(BITMAP *dest, int frame, int scroll)
{
    unsigned int y;
    unsigned int x;
    unsigned int end = scroll / 16 + 21;

    if (end > mLength)
    {
        end = mLength;
    }

    for (y = 0; y < 15; y++)
    {    
        for (x = scroll / 16; x < end; x++)
        {
            mMap[y * mLength + x]->draw(dest, frame, scroll);
        }
    }
}

std::ostream &operator<<(std::ostream &os, TileMap &o)
{
    os << "Tilemap: length = " << o.mLength << std::endl;
   
    return os;
}

void TileMap::logic()
{
    unsigned int i;
    for (i = 0; i < mMap.size(); i++)
    {
        mMap[i]->logic();
    }		
	
	++mFrame;

	if (mFrame % 3 == 0)
	{
		updateFire();
	}
}

void TileMap::updateFire()
{
	int x, y;

	BITMAP *temp = create_bitmap(16, 16);

	for (x = 1; x < 5; x++) {
		int r = rand() % 256;
		int c;
		if (r < 100) {
			c = makecol(255, 0, 0);
		} else if (r < 200) {			
			c = makecol(255, 255, 0);
		} else {			
			c = makecol(255, 255, 255);
		}
		putpixel(mFireTemp, rand()%16, rand()%6 + 10, c);
	}

	for (x = 0; x < 16; x++) {
		for (y = 0; y < 16; y++) {
			int c = getpixel(mFireTemp, x + (rand() % 3) - 1, y + rand() % 2);
			
			if (c == -1) c = 0;

			int r, g, b;
			r = getr(c); g = getg(c); b = getb(c);

			putpixel(temp, x, y, makecol(r * 0.93, g * 0.75, b * 0.65));
		}
	}

	blit(temp, mFireTemp, 0, 0, 0, 0, 16, 16);

	destroy_bitmap(temp);

	for (x = 1; x < 15; x++) {
		int r = rand() % 256;
		int c;
		if (r < 100) {
			c = makecol(255, 0, 0);
		} else if (r < 200) {			
			c = makecol(255, 255, 0);
		} else {			
			c = makecol(255, 255, 255);
		}
		
		putpixel(mFireTemp, x, 15, c);
	}

	for (x = 0; x < 16; x++) {
		for (y = 0; y < 16; y++) {
			int c = getpixel(mFireTemp, x, y);
			if (getr(c) > (15 - y) * 2) {
				putpixel(mFire, x, y, c);
			} else {
				putpixel(mFire, x, y, makecol(255, 0, 255));
			}
		}
	}
}

void TileMap::connectDoorButtonsAndDoors()
{
    unsigned int i;
    for (i = 0; i < mDoors.size(); i++)
    {
        unsigned int j;
        for (j = 0; j < mDoorButtons.size(); j++)
        {
            mDoorButtons[j]->addDoorTile(mDoors[i]);
        }
    }
}
